package Rangel.firebrigade.cluster;

import Rangel.common.convex.ConvexObject;
import Rangel.common.convex.CompositeConvexHull;
import Rangel.common.convex.IConvexHull;
import math.geom2d.Point2D;
import rescuecore2.standard.entities.StandardEntity;

import java.awt.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public abstract class Cluster {

    protected int id;
    protected Set<StandardEntity> entities;
    protected List<StandardEntity> newEntities;
    protected Set<StandardEntity> borderEntities;
    protected Set<StandardEntity> ignoredBorderEntities;
    protected List<StandardEntity> allEntities; //Roads and Buildings

    protected Point center;
    protected ConvexObject convexObject;
    protected IConvexHull convexHull;

    protected Polygon smallBorderPolygon;
    protected Polygon bigBorderPolygon;

    protected boolean isOverCenter;
    protected boolean isEdge;
    protected boolean isDying;
    protected boolean controllable;

    protected double value;

    protected Cluster() {
        entities = new HashSet<>();
        newEntities = new ArrayList<>();
        borderEntities = new HashSet<>();
        allEntities = new ArrayList<>();
        convexHull = new CompositeConvexHull();
        ignoredBorderEntities = new HashSet<>();
        bigBorderPolygon = new Polygon();
        smallBorderPolygon = new Polygon();
        convexObject = new ConvexObject();
        isOverCenter = false;
        isDying = true;
        controllable = true;
        isEdge = true;
    }

    public void addAll(List<StandardEntity> entities) {
        this.entities.addAll(entities);
    }

    public void add(StandardEntity entity) {
        entities.add(entity);
    }

    public void removeAll(List<StandardEntity> entities) {
        entities.removeAll(entities);
    }

    public void remove(StandardEntity entity) {
        entities.remove(entity);
    }


    public void eat(Cluster cluster) {
        if (!cluster.isDying()) {
            this.isDying = cluster.isDying();
        }
        if (!cluster.isEdge()) {
            this.isEdge = cluster.isEdge();
        }
        entities.addAll(cluster.getEntities());
    }


    public Set<StandardEntity> getEntities() {
        return entities;
    }

    public Point getCenter() {
        return center;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public ConvexObject getConvexHullObject() {
        return convexObject;
    }

    public Set<StandardEntity> getBorderEntities() {
        return borderEntities;
    }

    public abstract void updateConvexHull();

    public static Polygon scalePolygon(Polygon sourcePolygon, double scale) {
        Polygon scaledPolygon;
        int[] xs = new int[sourcePolygon.npoints];
        int[] ys = new int[sourcePolygon.npoints];
        Point2D p, p1;

        for (int i = 0; i < sourcePolygon.npoints; i++) {
            p = new Point2D(sourcePolygon.xpoints[i], sourcePolygon.ypoints[i]);
            p1 = p.scale(scale);
            xs[i] = (int) p1.getX();
            ys[i] = (int) p1.getY();
            p.clone();
        }

        Polygon preScaledPolygon = new Polygon(xs, ys, sourcePolygon.npoints);
        scaledPolygon = reAllocatePolygon(preScaledPolygon, sourcePolygon);
        if (scaledPolygon == null)
            scaledPolygon = preScaledPolygon;
        return scaledPolygon;
    }

    protected static Polygon reAllocatePolygon(Polygon scaled, Polygon source) {
        if (source == null || scaled == null || source.npoints == 0 || scaled.npoints == 0)
            return null;
        Polygon reAllocated;
        int[] xs = new int[scaled.npoints];
        int[] ys = new int[scaled.npoints];

        int sourceCenterX = 0;
        int sourceCenterY = 0;

        int scaledCenterX = 0;
        int scaledCenterY = 0;

        for (int i = 0; i < scaled.npoints; i++) {
            sourceCenterX += source.xpoints[i];
            sourceCenterY += source.ypoints[i];

            scaledCenterX += scaled.xpoints[i];
            scaledCenterY += scaled.ypoints[i];
        }

        sourceCenterX = sourceCenterX / source.npoints;
        sourceCenterY = sourceCenterY / source.npoints;

        scaledCenterX = scaledCenterX / scaled.npoints;
        scaledCenterY = scaledCenterY / scaled.npoints;

        int xDistance = sourceCenterX - scaledCenterX;
        int yDistance = sourceCenterY - scaledCenterY;

        for (int i = 0; i < scaled.npoints; i++) {
            xs[i] = scaled.xpoints[i] + xDistance;
            ys[i] = scaled.ypoints[i] + yDistance;
        }
        reAllocated = new Polygon(xs, ys, scaled.npoints);

        return reAllocated;
    }

    public boolean isDying() {
        return isDying;
    }

    public void setDying(boolean dying) {
        isDying = dying;
    }

    public boolean isControllable() {
        return controllable;
    }

    public void setControllable(boolean controllable) {
        this.controllable = controllable;
    }

    public boolean isEdge() {
        return isEdge;
    }

    public void setEdge(boolean edge) {
        isEdge = edge;
    }

    public void setAllEntities(List<StandardEntity> allEntities) {
        this.allEntities = allEntities;
    }

    public double getValue() {
        return value;
    }

    public Set<StandardEntity> getIgnoredBorderEntities() {
        return ignoredBorderEntities;
    }

}
